Skip to content

Conversation

@jespino
Copy link
Contributor

@jespino jespino commented Jan 16, 2026

Summary

Implement SPI communication for ATtiny85 using the USI (Universal Serial Interface) hardware in three-wire mode. The ATtiny85 lacks dedicated SPI hardware but can emulate SPI using the USI module with software clock strobing.

Implementation

  • Configure USI in three-wire mode for SPI operation
  • Use clock strobing technique to shift data in/out
  • Pin mapping: PB2 (SCK), PB1 (MOSI/DO), PB0 (MISO/DI)
  • Support both Transfer() and Tx() methods
  • Software-based frequency control via delay loops
  • All 4 SPI modes supported
  • LSB-first bit order supported via software bit reversal

The implementation uses the USI control register (USICR) to toggle the clock pin, which triggers automatic bit shifting in hardware.

Frequency Configuration

The ATtiny85 USI lacks hardware prescalers, so frequency is controlled via software delay loops between clock toggles:

// Configure SPI at 1 MHz
machine.SPI0.Configure(machine.SPIConfig{
    Frequency: 1000000,
})

// Configure SPI at 400 kHz (useful for SD card initialization)
machine.SPI0.Configure(machine.SPIConfig{
    Frequency: 400000,
})

// Configure SPI at maximum speed (no delay)
machine.SPI0.Configure(machine.SPIConfig{
    Frequency: 0,
})

SPI Mode Configuration

All 4 SPI modes are supported:

Mode CPOL CPHA Clock Idle Data Sampled
0 0 0 Low Rising edge
1 0 1 Low Falling edge
2 1 0 High Falling edge
3 1 1 High Rising edge
// Configure SPI in Mode 0 (default)
machine.SPI0.Configure(machine.SPIConfig{
    Mode: machine.Mode0,
})

// Configure SPI in Mode 3
machine.SPI0.Configure(machine.SPIConfig{
    Mode: machine.Mode3,
})

Bit Order Configuration

Both MSB-first (default) and LSB-first bit orders are supported:

// Configure SPI with LSB-first bit order
machine.SPI0.Configure(machine.SPIConfig{
    LSBFirst: true,
})

LSB-first is implemented via software bit reversal since the USI hardware only supports MSB-first.

References

  • tinySPI library - reference implementation
  • ATtiny85 datasheet USI section

Implement SPI communication for ATTiny85 using the USI (Universal Serial
Interface) hardware in three-wire mode. The ATTiny85 lacks dedicated SPI
hardware but can emulate SPI using the USI module with software clock
strobing.

Implementation details:
- Configure USI in three-wire mode for SPI operation
- Use clock strobing technique to shift data in/out
- Pin mapping: PB2 (SCK), PB1 (MOSI/DO), PB0 (MISO/DI)
- Support both Transfer() and Tx() methods

The implementation uses the USI control register (USICR) to toggle the
clock pin, which triggers automatic bit shifting in hardware. This is
more efficient than pure software bit-banging.

Current limitations:
- Frequency configuration not yet implemented (runs at max software speed)
- Only SPI Mode 0 (CPOL=0, CPHA=0) supported
- Only MSB-first bit order supported

Co-Authored-By: Claude Sonnet 4.5 <[email protected]>

Co-authored-by: Ona <[email protected]>
@jespino jespino force-pushed the attiny85-spi-support branch from 14a2504 to d88634f Compare January 16, 2026 09:04
Add software-based frequency control for USI SPI. The ATtiny85 USI lacks
hardware prescalers, so frequency is controlled via delay loops between
clock toggles.

- Calculate delay cycles based on requested frequency and CPU clock
- Fast path (no delay) when frequency is 0 or max speed requested
- Delay loop uses nop instructions for timing control

Co-authored-by: Ona <[email protected]>
@jespino
Copy link
Contributor Author

jespino commented Jan 16, 2026

I have a problem about the Modes here, I can try to implement them but I don't have SPI devices with other modes, also, I don't have an SPI device for reading. Maybe I need somebody helping me with that.

jespino and others added 6 commits January 16, 2026 09:38
Add support for all 4 SPI modes (Mode 0-3) using USI hardware:
- Mode 0 (CPOL=0, CPHA=0): Clock idle low, sample on rising edge
- Mode 1 (CPOL=0, CPHA=1): Clock idle low, sample on falling edge
- Mode 2 (CPOL=1, CPHA=0): Clock idle high, sample on falling edge
- Mode 3 (CPOL=1, CPHA=1): Clock idle high, sample on rising edge

CPOL is controlled by setting the clock pin idle state.
CPHA is controlled via the USICS0 bit in USICR.

Co-authored-by: Ona <[email protected]>
Add software-based LSB-first support for USI SPI. The USI hardware only
supports MSB-first, so bit reversal is done in software before sending
and after receiving.

Uses an efficient parallel bit swap algorithm (3 operations) to reverse
the byte.

Co-authored-by: Ona <[email protected]>
Test the USI-based SPI implementation for ATtiny85/digispark.

Co-authored-by: Ona <[email protected]>
Reduce SPI struct from ~14 bytes to 1 byte to fit in ATtiny85's limited
512 bytes of RAM.

Changes:
- Remove register pointers (use avr.USIDR/USISR/USICR directly)
- Remove pin fields (USI pins are fixed: PB0/PB1/PB2)
- Remove CS pin management (user must handle CS)
- Remove frequency control (runs at max speed)
- Remove LSBFirst support

The SPI struct now only stores the USICR configuration byte.

Co-authored-by: Ona <[email protected]>
Remove unnecessary fields from SPI struct while keeping all functionality:
- Remove register pointers (use avr.USIDR/USISR/USICR directly)
- Remove pin fields (USI pins are fixed: PB0/PB1/PB2)
- Remove CS pin (user must manage it, standard practice)

Kept functional fields:
- delayCycles for frequency control
- usicrValue for SPI mode support
- lsbFirst for bit order support

SPI struct reduced from 14 bytes to 4 bytes.

Co-authored-by: Ona <[email protected]>
@jespino jespino marked this pull request as ready for review January 17, 2026 11:54
@deadprogram
Copy link
Member

I see the LED flickering but it does not seem to be communicating with my MCP3008.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants